#include "ffvencoder.h"
#include"queue/ffvframequeue.h"
#include"queue/ffvpacketqueue.h"

FFVEncoder::FFVEncoder()
{

}

FFVEncoder::~FFVEncoder()
{
    if(codecCtx){
        avcodec_free_context(&codecCtx);
    }
    if(vPars){
        delete vPars;
        vPars = nullptr;
    }
}

void FFVEncoder::init(FFVPacketQueue *pktQueue_){
    pktQueue = pktQueue_;
}

void FFVEncoder::initVideo(AVFrame *frame,AVRational fps)
{
    vPars = new FFVEncoderPars();
    vPars->biteRate = 2 * 1024 * 1024; //500k
    vPars->width = frame->width;
    vPars->height = frame->height;
    vPars->videoFmt = AV_PIX_FMT_YUV420P;
    vPars->frameRate = fps;

    AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if(codec == nullptr){
        std::cerr << "Find H264 Codec Fail !" << std::endl;
        return;
    }

    codecCtx = avcodec_alloc_context3(codec);
    if(codecCtx == nullptr){
        std::cerr << "Alloc CodecCtx Fail !" << std::endl;
        return;
    }

    codecCtx->width = vPars->width;
    codecCtx->height = vPars->height;
    codecCtx->framerate = vPars->frameRate;
    codecCtx->time_base = AVRational{vPars->frameRate.den,vPars->frameRate.num};
    codecCtx->pix_fmt = vPars->videoFmt;

#if 0
    codecCtx->max_b_frames = 0;  // 禁用B帧
     codecCtx->gop_size = 12;     // 合理关键帧间隔
     codecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY;  // 低延迟模式
#endif

    int ret = avcodec_open2(codecCtx,codec,nullptr);
    if(ret < 0){
        printError(ret);
        return;
    }

}

int FFVEncoder::encode(AVFrame *frame, int streamIndex, int64_t pts, AVRational timeBase)
{
    if(frame == nullptr) {
        return -1;
    }

    pts = av_rescale_q(pts,timeBase,codecCtx->time_base);
    if(pts <= lastPts){ //修正
        pts = lastPts + 1;
    }
    lastPts = pts;

    std::cout<<"rescale pts:"<<pts<<std::endl;
    AVFrame* cloneFrame = av_frame_clone(frame);
    cloneFrame->pts = pts;


    int ret = avcodec_send_frame(codecCtx,cloneFrame);
    if(ret < 0){
        printError(ret);
        av_frame_unref(cloneFrame);
        av_frame_free(&cloneFrame);
        return -1;
    }


    while(1){
        AVPacket* pkt = av_packet_alloc();
        ret = avcodec_receive_packet(codecCtx,pkt);
        if(ret == AVERROR(EAGAIN)){
            av_packet_free(&pkt);

//            std::cerr<<"Encode Video AVERROR(EAGAIN)" << std::endl;
            break;
        }
        else if(ret == AVERROR_EOF){
            std::cout << "Encode Video EOF !" << std::endl;
            av_packet_free(&pkt);
            break;
        }
        else if(ret < 0){
//            std::cerr << "Encode Video Frame Fail !" << std::endl;
            printError(ret);
            av_packet_free(&pkt);
            av_frame_unref(cloneFrame);
            av_frame_free(&cloneFrame);
            return -1;
        }
        else{
            pkt->stream_index = streamIndex;
//            std::cerr<<"enqueue pkt !"<<std::endl;
            std::cout<<"video packet pts:"<<pkt->pts<<std::endl;
            std::cout<<"video packet dts:"<<pkt->dts<<std::endl;
            std::cout<<"video packet duration:"<<pkt->duration<<std::endl;
            pktQueue->enqueue(pkt);
            av_packet_free(&pkt);
        }
    }

    av_frame_unref(cloneFrame);
    av_frame_free(&cloneFrame);

    return 0;
}

AVCodecContext *FFVEncoder::getCodecCtx()
{
    return codecCtx;
}

FFVEncoderPars *FFVEncoder::getEncoderPars()
{
    return vPars;
}

void FFVEncoder::printError(int ret)
{
    char errorBuffer[AV_ERROR_MAX_STRING_SIZE];
    int res = av_strerror(ret,errorBuffer,sizeof errorBuffer);
    if(res < 0){
        std::cerr << "Unknow Error!" << std::endl;
    }
    else{
        std::cerr << "Error:" << errorBuffer << std::endl;
    }
}
